package com.daxiang.utils;

import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.http.HttpMethodName;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.*;
import com.qcloud.cos.region.Region;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.Date;
import java.util.List;

@Component
public class COSUtil {
    // 文件链接过期时间30分钟
    public static final Long EXPIRATION_TIME = 30L * 60L * 1000L;

    private static String bucketName;

    private static String region;

    private static String secretId;

    private static String secretKey;

    private static volatile COSClient sCosClient;

    @Data
    public static class CosFile {
        public String ETag;
        public String path;
        public String url;
        public Date urlExpiredTime;
        public long size;
        public Date lastModified;
    }

    private static synchronized COSClient getCosClient() {
        if (null == sCosClient) {
            COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
            // clientConfig 中包含了设置 region, https(默认 http), 超时, 代理等 set 方法
            ClientConfig clientConfig = new ClientConfig(new Region(region));
            clientConfig.setHttpProtocol(HttpProtocol.https);
            clientConfig.setSignExpired(EXPIRATION_TIME);
            sCosClient = new COSClient(cred, clientConfig);
        }
        return sCosClient;
    }

    private static synchronized void shutdownClient() {
        if (null != sCosClient) {
            sCosClient.shutdown();
            sCosClient = null;
        }
    }

    private static boolean isFileExists(String key) {
        try {
            ObjectMetadata objectMetadata = getCosClient().getObjectMetadata(bucketName, key);
            return null != objectMetadata;
        } catch (CosServiceException e) {
            return false;
        }
    }

    public static CosFile uploadFile(File file, String path) {
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, path, file);
        PutObjectResult result = getCosClient().putObject(putObjectRequest);
        CosFile cosFile = new CosFile();
        cosFile.ETag = result.getETag();
        cosFile.path = path;
        if (null != result.getMetadata() && result.getMetadata().getContentLength() != 0) {
            cosFile.size = result.getMetadata().getContentLength();
        } else {
            cosFile.size = file.length();
        }
        if (null != result.getMetadata() && null != result.getMetadata().getLastModified()) {
            cosFile.lastModified = result.getMetadata().getLastModified();
        } else {
            cosFile.lastModified = new Date(System.currentTimeMillis());
        }
        // 生成临时访问/下载链接
        cosFile.urlExpiredTime = new Date(System.currentTimeMillis() + EXPIRATION_TIME);
        GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(bucketName, path, HttpMethodName.GET);
        cosFile.url = getCosClient().generatePresignedUrl(req).toString();
        shutdownClient();
        return cosFile;
    }

    public static String moveFile(String srcPath, String destPath)
            throws FileNotFoundException, FileConflictException {
        return moveFile(srcPath, destPath, true);
    }

    /**
     * 移动文件
     *
     * @param originPath 源文件路径
     * @param destPath  目标文件路径
     * @param overwrite 如果目标路径文件存在，是否覆盖
     * @return 返回移动后的文件预签名链接
     * @throws FileNotFoundException 源文件不存在
     * @throws FileConflictException 目标文件已存在
     */
    public static String moveFile(String originPath, String destPath, boolean overwrite)
            throws FileNotFoundException, FileConflictException {
        if (!isFileExists(originPath)) {
            shutdownClient();
            throw new FileNotFoundException("file '" + originPath +"' not found!");
        } else {
            if (overwrite || !isFileExists(destPath)) {
                CopyObjectRequest request = new CopyObjectRequest(bucketName, originPath, bucketName, destPath);
                getCosClient().copyObject(request);
                getCosClient().deleteObject(bucketName, originPath);
                shutdownClient();
                return getSignedUrl(destPath);
            } else {
                throw new FileConflictException("file '" + destPath + "' already exists!");
            }
        }
    }

    public static void deleteFile(String path) {
        getCosClient().deleteObject(bucketName, path);
        shutdownClient();
    }

    public static void deleteFiles(List<String> pathList) {
        COSClient client = getCosClient();
        for (String path : pathList) {
            client.deleteObject(bucketName, path);
        }
        shutdownClient();
    }

    private static String getSignedUrl(String key) {
        GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(bucketName, key, HttpMethodName.GET);
        // 设置签名过期时间(可选), 若未进行设置, 则默认使用 ClientConfig 中的签名过期时间(1小时)
        Date expirationDate = new Date(System.currentTimeMillis() + EXPIRATION_TIME);
        req.setExpiration(expirationDate);
        return getCosClient().generatePresignedUrl(req).toString();
    }

    public static String getFileSignedUrl(String key) {
        String url = null;
        if (isFileExists(key)) {
            url = getSignedUrl(key);
        }
        shutdownClient();
        return url;
    }

    /**
     * 找不到COS文件异常
     */
    public static class FileNotFoundException extends RuntimeException {
        public FileNotFoundException() {
            super();
        }

        public FileNotFoundException(String message) {
            super(message);
        }
    }

    /**
     * 文件冲突异常，当上传/复制文件的目标路径已存在时，抛出此异常
     */
    public static class FileConflictException extends RuntimeException {
        public FileConflictException() {
            super();
        }

        public FileConflictException(String message) {
            super(message);
        }
    }

    @Value("${cos.bucketName}")
    public void setBucketName(String bucketName) {
        COSUtil.bucketName = bucketName;
    }

    @Value("${cos.region}")
    public void setRegion(String region) {
        COSUtil.region = region;
    }

    @Value("${cos.secretId}")
    public void setSecretId(String secretId) {
        COSUtil.secretId = secretId;
    }

    @Value("${cos.secretKey}")
    public void setSecretKey(String secretKey) {
        COSUtil.secretKey = secretKey;
    }
}
